package com.ncell.cell;

import com.ncell.utils.Utils;
import java.util.Collections;
import java.util.List;

public class UnitProcessor {

    private final float weightDecreasing = -0.01f;
    private final float weightIncreasing = 0.05f;
    private List<I> is;
    private List<P> ps;
    private PIUnit unit;

    public void setPUnit(PIUnit unit) {
        this.unit = unit;
        is = unit.getIs();
        ps = unit.getPs();
    }

    public void processStep() {
        resetBeforeStep();
        initGravityOnP();
        Collections.sort(is);
        for (I i : is) {
            processIActivation(i);
        }

        /*  for (I i : is) {
         processIIngibition(i);
         }*/

        for (I i : is) {
            processIMoving(i);
        }

        for (I i : is) {
            processIPostProcessing(i);
        }
    }

    private boolean isPConnectionActive(PConnection connection) {
        P p = connection.getP();
        if (connection.isNegativeConnection()) {
            if (p.getAggrievedness() > 0.5f) {
                return true;
            }
        } else {
            float activation = p.getActivation();
            if (activation > 0.5f) {
                return true;
            }
        }

        return false;
    }

    private void setOwnPriorityToP(I i) {
        float priority = i.getPriority();
        for (PConnection connection : i.getPField()) {
            if (connection.getP().getPriority() < priority) {
                connection.getP().setOwner(i);
                connection.getP().setPriority(priority);
            }
        }
    }

    //нужно увеличивать притягательность Р, которые входят в состав активного И, но почему-то неактивны в данный момент
    private void increaseGravityOnInactiveP(I i) {
        if (i.getActivation() >= 0.99) {//all P is active, skip
            return;
        }
        if (i.getActivation() <= 0.5) {//to much inactive P. Skip
            return;
        }

        for (PConnection pc : i.getPField()) {
            if (!isPConnectionActive(pc)) {
                pc.getP().setPriority(0);
                pc.getP().setAggrievedness(1);
            }
        }
    }

    private void processIIngibition(I i) {
        /* int active = 0;
         for (PConnection connection : i.getPField()) {
         if (connection.getP().getPriority() <= i.getPriority() && isPConnectionActive(connection)) {
         active++;
         }
         }
         if (active == 0) {
         ingibiteI(i, 0.05f);
         }*/
    }

    //нужно переписать все с учетом энергии отдаваемой р(р отдает свою энергию через связь. Чем она крепче, тем больше отдает, и на остальные остается все меньше и меньше)
    private void processIPostProcessing(I i) {
        int active = 0;
        for (PConnection connection : i.getPField()) {
            if (connection.getP().getPriority() <= i.getPriority() && isPConnectionActive(connection)) {
                active++;
            }
        }
        List<PConnection> connections = i.getPField();
        for (int index = 0; index < connections.size(); index++) {
            PConnection connection = connections.get(index);
            if (active == 0) {
                connection.addWeight(weightDecreasing);
                if (connection.getWeight() < 0) {
                    i.removeConnection(connection);
                    index--;
                }
            } else if (connection.getP().isActive() && !connection.isNegativeConnection()) {
                connection.addWeight(weightIncreasing);
            } else if (connection.getP().getAggrievedness() > 0.5f && connection.isNegativeConnection()) {
                connection.addWeight(weightIncreasing);
            } else {
                connection.addWeight(weightDecreasing);
                if (connection.getWeight() < 0) {
                    i.removeConnection(connection);
                    index--;
                }
            }
        }
    }

    private void processIMoving(I i) {
        if (i.canMove()) {
            float minLength = 99999999999999f;
            P pcandidate = null;
            for (P p : unit.getPs()) {
                if (p.getPriority() <= i.getPriority() && p.getCalculatedGravity() >= 0.6) {
                    if (!i.containsP(p)) {
                        float length = Utils.length(i.getAx(), i.getAy(), p.getX(), p.getY());
                        if (minLength > length) {
                            minLength = length;
                            pcandidate = p;
                        }
                    }
                }
            }

            if (pcandidate == null) {
                return;
            }

            moveIToP(i, pcandidate);
        }
    }

    private void moveIToP(I i, P p) {
        float angle = (float) Math.atan2(p.getY() - i.getAy(), p.getX() - i.getAx());
        float length = Utils.length(i.getAx(), i.getAy(), p.getX(), p.getY());
        float step = length;
        if (step > 1) {
            step = 1;
        }
        step *= i.getMoveDesire();
        if (step > 0.5f) {
            step = 0.5f;
        }
        float nx = (float) (Math.cos(angle) * step + i.getAx());
        float ny = (float) (Math.sin(angle) * step + i.getAy());
        i.setAx(nx);
        i.setAy(ny);
        length = Utils.length(i.getAx(), i.getAy(), p.getX(), p.getY());
        if (length <= 0.2) {
            PConnection pconnection = new PConnection(p).setWeight(0.4f);
            if (!p.isActive()) {
                pconnection.setNegativeConnection(true);
            }

            i.addConnection(pconnection);
            p.setGravityIngibition(0.3f);
            p.setPriority(i.getPriority());
        }
    }

    private float calculateIBaseActivation(I i) {
        //TODO: сделать подсчет зависимым от силы связи. Если связь слабая, то она слабо действует на активацию
        List<PConnection> connections = i.getPField();
        int inactive = 0;
        int active = 0;
        float fullPossibleActivation = 0;
        float currentActivation = 0;
        for (PConnection c : connections) {
            fullPossibleActivation += c.getWeight();
            float ta = 0;
            if (c.isNegativeConnection()) {
                ta = c.getWeight() * c.getP().getAggrievedness();
            } else {
                ta = c.getWeight() * c.getP().getActivation();
            }

            //if p is not active, it will ingibite activation
            if (ta <= c.getWeight() / 2) {
                ta = ta * -1;
            }

            currentActivation += ta;
        }

        float activation = 0;
        activation = currentActivation / fullPossibleActivation;
        if (Float.isNaN(activation)) {
            activation = 0;
        }

        i.setActivation(activation);
        float minMoveDesire = (0.2f - fullPossibleActivation);
        if (minMoveDesire < 0) {
            minMoveDesire = 0;
        }

        float moveDesire = currentActivation / fullPossibleActivation;
        if (Float.isNaN(moveDesire)) {
            moveDesire = 0;
        }

        moveDesire += minMoveDesire;
        if (moveDesire < 0) {
            moveDesire = 0;
        }

        if (moveDesire > 1) {
            moveDesire = 1;
        }

        //float moveDesire = (active - (inactive * 1.5f)) + 0.2f;
        i.setMoveDesire(moveDesire);
        if (connections.isEmpty()) {
            return activation;
        }

        return activation;
    }

    private void processIActivation(I i) {
        calculateIBaseActivation(i);
        if (i.isActive()) {
            setOwnPriorityToP(i);
            increaseGravityOnInactiveP(i);
        }
    }

    private void initGravityOnP() {
        for (P p : unit.getPs()) {
            if (p.isActive()) {
                p.setGravity(1);
            } else {
                p.setGravity(0);
            }
        }
    }

    private void resetBeforeStep() {
        for (I i : is) {
            i.setActivation(0);
            i.setMoveDesire(0);
        }

        for (P p : ps) {
            p.setOwner(null);
            p.setAggrievedness(0);
            p.setPriority(-9999);
            p.setGravityIngibition(0);
            if (p.isActive()) {
                p.setGravity(1);
            }
        }
    }
}
